use crate::ast::*;
use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::values::BasicValueEnum;

impl<'ctx> LLVMBackend<'ctx> {


    /// Generate unwrap() method - panics if None
    pub(in crate::llvm_backend) fn generate_option_unwrap(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        _location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        let tag = self.extract_option_tag(option_value)?;
        let is_some = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            self.context.i32_type().const_zero(),
            "is_some_check",
        )?;

        let current_fn = self.current_function.unwrap();
        let some_block = self.context.append_basic_block(current_fn, "some_block");
        let none_block = self.context.append_basic_block(current_fn, "none_block");
        let continue_block = self
            .context
            .append_basic_block(current_fn, "continue_block");

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: extract and return value
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;
        let some_llvm_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let result_alloca = self.builder.build_alloca(some_llvm_type, "unwrap_result")?;
        self.builder.build_store(result_alloca, some_value)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // None block: panic
        self.builder.position_at_end(none_block);
        let panic_msg = self
            .context
            .const_string(b"called unwrap on Option::None", false);
        let panic_msg_ptr = self
            .builder
            .build_alloca(panic_msg.get_type(), "panic_msg")?;
        self.builder.build_store(panic_msg_ptr, panic_msg)?;

        // Call panic function
        if let Some(panic_fn) = self.module.get_function("panic") {
            self.builder
                .build_call(panic_fn, &[panic_msg_ptr.into()], "panic_call")?;
        }
        self.builder.build_unreachable()?;

        // Continue block
        self.builder.position_at_end(continue_block);
        let result = self
            .builder
            .build_load(some_llvm_type, result_alloca, "unwrap_value")?;
        Ok(result)
    }


    /// Generate unwrap_or() method
    pub(in crate::llvm_backend) fn generate_option_unwrap_or(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        arguments: &[Expression],
        _location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::internal("unwrap_or expects 1 argument"));
        }

        let default_value = self.generate_expression(&arguments[0])?;
        let tag = self.extract_option_tag(option_value)?;
        let is_some = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            self.context.i32_type().const_zero(),
            "is_some_check",
        )?;

        let current_fn = self.current_function.unwrap();
        let some_block = self.context.append_basic_block(current_fn, "some_block");
        let none_block = self.context.append_basic_block(current_fn, "none_block");
        let continue_block = self
            .context
            .append_basic_block(current_fn, "continue_block");

        // Allocate result
        let some_llvm_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let result_alloca = self
            .builder
            .build_alloca(some_llvm_type, "unwrap_or_result")?;

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: extract value
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;
        self.builder.build_store(result_alloca, some_value)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // None block: use default
        self.builder.position_at_end(none_block);
        self.builder.build_store(result_alloca, default_value)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block
        self.builder.position_at_end(continue_block);
        let result = self
            .builder
            .build_load(some_llvm_type, result_alloca, "unwrap_or_value")?;
        Ok(result)
    }


    /// Generate expect() method - panics with custom message if None
    pub(in crate::llvm_backend) fn generate_option_expect(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::internal(
                "expect expects 1 argument (message)",
            ));
        }

        let tag = self.extract_option_tag(option_value)?;
        let is_some = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            self.context.i32_type().const_zero(),
            "is_some_check",
        )?;

        let current_fn = self.current_function.unwrap();
        let some_block = self.context.append_basic_block(current_fn, "some_block");
        let none_block = self.context.append_basic_block(current_fn, "none_block");
        let continue_block = self
            .context
            .append_basic_block(current_fn, "continue_block");

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: extract and return value
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;
        let some_llvm_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let result_alloca = self.builder.build_alloca(some_llvm_type, "expect_result")?;
        self.builder.build_store(result_alloca, some_value)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // None block: panic with custom message
        self.builder.position_at_end(none_block);
        let panic_expr = crate::ast::PanicExpr {
            value: Box::new(arguments[0].clone()),
            location: *location,
        };
        let _ = self.generate_panic_expression(&panic_expr)?;

        // Continue block
        self.builder.position_at_end(continue_block);
        let result = self
            .builder
            .build_load(some_llvm_type, result_alloca, "expect_value")?;
        Ok(result)
    }


    /// Generate unwrap_or_else() method - returns value or calls function
    pub(in crate::llvm_backend) fn generate_option_unwrap_or_else(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::internal(
                "unwrap_or_else expects 1 argument (function)",
            ));
        }

        let tag = self.extract_option_tag(option_value)?;
        let is_some = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            self.context.i32_type().const_zero(),
            "is_some_check",
        )?;

        let current_fn = self.current_function.unwrap();
        let some_block = self.context.append_basic_block(current_fn, "some_block");
        let none_block = self.context.append_basic_block(current_fn, "none_block");
        let continue_block = self
            .context
            .append_basic_block(current_fn, "continue_block");

        // Allocate result
        let some_llvm_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let result_alloca = self
            .builder
            .build_alloca(some_llvm_type, "unwrap_or_else_result")?;

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: extract value
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;
        self.builder.build_store(result_alloca, some_value)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // None block: call function
        self.builder.position_at_end(none_block);
        let call_expr = Expression::Call(crate::ast::CallExpr {
            callee: Box::new(arguments[0].clone()),
            arguments: vec![],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });
        let computed_value = self.generate_expression(&call_expr)?;
        self.builder.build_store(result_alloca, computed_value)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block
        self.builder.position_at_end(continue_block);
        let result =
            self.builder
                .build_load(some_llvm_type, result_alloca, "unwrap_or_else_value")?;
        Ok(result)
    }

}
